package com.hp.bon.sgw.convert;

import com.google.gson.Gson;
import com.hp.bon.sgw.domain.ConvMapInfo;
import com.hp.bon.sgw.service.ConfigListener;
import com.hp.bon.sgw.service.ConfigService;
import com.hp.bon.sgw.start.SpringContext;
import com.hp.bon.sgw.util.DicXmlExport;
import com.hp.ocs.diameter.message.DictionaryManager;
import com.hp.ocs.diameter.message.Message;
import java.io.InputStream;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import javax.ws.rs.Path;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Path("/")
public class Xml2AVPConvetor
  implements ConfigListener
{
  private static Xml2AVPConvetor instance;
  private static final Logger logger = LoggerFactory.getLogger(Xml2AVPConvetor.class);

  private static final Integer sgwRootAVPCode = Integer.valueOf(99999);
  private final Set<String> monitorConfKeys = new LinkedHashSet();

  private volatile Map<String, ConvMapInfo> allNamesMap = Collections.emptyMap();

  private volatile Map<Integer, ConvMapInfo> allCodesMap = Collections.emptyMap();

  private static Map<Integer, ConvMapInfo> commonAVPToXMLMap = new LinkedHashMap();

  private volatile Map<String, Map<Integer, ConvMapInfo>> avpToXMLMappings = Collections.emptyMap();

  private volatile Map<String, Map<String, ConvMapInfo>> xmlToAVPMappings = Collections.emptyMap();

  static
  {
    ConvMapInfo avpInfo = null;
    avpInfo = new ConvMapInfo("", 873, "Service_Information", "Grouped", 10415, "Service_Information", "");
    commonAVPToXMLMap.put(Integer.valueOf(avpInfo.getAvpCode()), avpInfo);
    avpInfo = new ConvMapInfo("", 20631, "Service_Result_Code", "Unsigned32", 81000, "Service_Result_Code", "");
    commonAVPToXMLMap.put(Integer.valueOf(avpInfo.getAvpCode()), avpInfo);
    avpInfo = new ConvMapInfo("", sgwRootAVPCode.intValue(), "SGW_DATA_ROOT", "Grouped", 81000, "SGW_DATA_ROOT", "");
    commonAVPToXMLMap.put(Integer.valueOf(avpInfo.getAvpCode()), avpInfo);
  }

  public static Xml2AVPConvetor getInstance()
  {
    if (instance == null) {
      instance = new Xml2AVPConvetor();
      instance.loadMappingRules();
    }
    return instance;
  }

  public Xml2AVPConvetor()
  {
    this.monitorConfKeys.add("SWGChannel.Xml2AvpMappings");
  }

  public Map<String, ConvMapInfo> getALLNamesMap() {
    return this.allNamesMap;
  }

  public Map<Integer, ConvMapInfo> getAllCodesMap() {
    return this.allCodesMap;
  }

  public Map<Integer, ConvMapInfo> findAVPToXMLMappings(String serviceCtxtID, String realm)
  {
    Map founded = (Map)this.avpToXMLMappings.get(serviceCtxtID);
    if (founded != null) {
      return founded;
    }

    String scid = serviceCtxtID.substring(0, serviceCtxtID.indexOf("@"));
    founded = (Map)this.avpToXMLMappings.get(scid);

    return founded != null ? founded : this.allCodesMap;
  }

  public Map<String, ConvMapInfo> findXMLToAVPMappings(String serviceCtxtID, String realm) {
    Map founded = (Map)this.xmlToAVPMappings.get(serviceCtxtID);
    if (founded != null) {
      return founded;
    }
    String scid = serviceCtxtID.substring(0, serviceCtxtID.indexOf("@"));
    founded = (Map)this.xmlToAVPMappings.get(scid);

    return founded != null ? founded : this.allNamesMap;
  }

  public void loadMappingRules() {
    loadDictionaryXml();
    logger.info("load defined  Xml2AvpMappings ");

    String xml2AVPMappingRul = "SWGChannel.Xml2AvpMappings";
    String mappingStr = SpringContext.getInstance().getConfigService().getValue(xml2AVPMappingRul, null);
    if (mappingStr == null) {
      logger.info("no customer Xml2AvpMappings defined " + xml2AVPMappingRul);
      return;
    }
    ConvMapInfo[] allMapInfos = null;
    try {
      allMapInfos = (ConvMapInfo[]) new Gson().fromJson(mappingStr, ConvMapInfo[].class);
    } catch (Exception e) {
      logger.error("parse config error " + xml2AVPMappingRul, e);
    }
    if (allMapInfos == null) {
      logger.info("no Xml2AvpMappings configed ");
      return;
    }
    logger.info("load " + xml2AVPMappingRul + " total records " + allMapInfos.length);

    this.avpToXMLMappings = new LinkedHashMap();
    this.xmlToAVPMappings = new LinkedHashMap();
    for (ConvMapInfo convMap : allMapInfos) {
      putToIntMap(this.avpToXMLMappings, convMap.getServiceCtxID(), Integer.valueOf(convMap.getAvpCode()), convMap);
      putToStringMap(this.xmlToAVPMappings, convMap.getServiceCtxID(), convMap.getXmlTag(), convMap);
    }
  }

	public String loadDictionaryXml() {
		logger.info("load dictionary.xml ,from classpath ");
		InputStream instream = Thread.currentThread().getContextClassLoader().getResourceAsStream("dictionary.xml");
		Collection<ConvMapInfo> allBeans = DicXmlExport.exportDicXMLAVPs(instream);
		Map<String, ConvMapInfo> namesMap = new LinkedHashMap<String, ConvMapInfo>();
		Map<Integer, ConvMapInfo> codesMap = new LinkedHashMap<Integer, ConvMapInfo>();
		for (ConvMapInfo conv : allBeans) {
			namesMap.put(conv.getAvpName(), conv);
			codesMap.put(conv.getAvpCode(), conv);
		}
		allNamesMap = namesMap;
		allCodesMap = codesMap;
		try {
			DictionaryManager.getInstance();
		} catch (Exception e) {
			logger.warn("DictionaryManager reload err:" + e);
		}
		return "SUCCESS";
	}

  private void putToIntMap(Map<String, Map<Integer, ConvMapInfo>> map, String key1, Integer key2, ConvMapInfo value) {
    Map valueMap = (Map)map.get(key1);
    if (valueMap == null) {
      logger.info("add key " + key1);
      valueMap = new LinkedHashMap();
      for (ConvMapInfo c : commonAVPToXMLMap.values()) {
        valueMap.put(Integer.valueOf(c.getAvpCode()), c);
      }
      map.put(key1, valueMap);
    }
    valueMap.put(key2, value);
  }

  private void putToStringMap(Map<String, Map<String, ConvMapInfo>> map, String key1, String key2, ConvMapInfo value)
  {
    Map valueMap = (Map)map.get(key1);
    if (valueMap == null) {
      valueMap = new LinkedHashMap();
      for (ConvMapInfo c : commonAVPToXMLMap.values()) {
        valueMap.put(c.getXmlTag(), c);
      }
      map.put(key1, valueMap);
    }
    valueMap.put(key2, value);
  }

  public Set<String> monitorConfKeys()
  {
    return this.monitorConfKeys;
  }

  public void onConfigUpdated(String updatedKey)
  {
    if (updatedKey.startsWith("SWGChannel.Xml2AvpMappings"))
      loadMappingRules();
  }
}